#include "mainwindow.h"
#include "ui_mainwindow.h"
#include  <QImage>
#include  <QDebug>
#include  <QDateTime>
#include  <QDir>
#include "v4l2.h"
#include "ffmpeg.h"


extern "C"{
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <math.h>
    #include <time.h>
    #include <libavutil/avassert.h>
    #include <libavutil/channel_layout.h>
    #include <libavutil/opt.h>
    #include <libavutil/mathematics.h>
    #include <libavutil/timestamp.h>
    #include <libavformat/avformat.h>
    #include <libswscale/swscale.h>
    #include <libswresample/swresample.h>
    #include <stdio.h>
    #include <sys/ioctl.h>
    #include <linux/videodev2.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <sys/mman.h>
    #include <poll.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <pthread.h>
    #include <signal.h>
    #include <unistd.h>
    #include <string.h>
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    , timer(new QTimer(this))
    , isRecording(false)
    , frameCount(0)
{
    ui->setupUi(this);

    QDir dir("/home/orangepi/saved_picture");
    if(!dir.exists()){
        dir.mkpath("/home/orangepi/saved_picture");
    }

    QDir videoDir("/home/orangepi/saved_video");
    if(!videoDir.exists()){
        videoDir.mkpath("/home/orangepi/saved_video");
    }
    if( v4l2_init() == -1 ){
        qDebug() << "Initialization v4l2  failed";
        exit(-1);
    }

    ui->stopButton->setEnabled(false); //stop button disable

    // Set up timer for frame capture (approx. 25 FPS)
    connect(timer, &QTimer::timeout, this, &MainWindow::captureAndDisplayFrame);
    timer->start(1000 / FPS);

    connect(ui->snapshotButton, &QPushButton::clicked, this, &MainWindow::snapshot);
    connect(ui->recordButton, &QPushButton::clicked, this, &MainWindow::startRecording);
    connect(ui->stopButton, &QPushButton::clicked, this, &MainWindow::stopRecording);

}

MainWindow::~MainWindow()
{
    timer->stop();
    ffmpeg_cleanup();
    if (fd >= 0) {
        enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
        ioctl(fd, VIDIOC_STREAMOFF, &type);
        for (auto &buf : global_buffers) {
            munmap(buf.start, buf.length);
        }
        ::close(fd);
    }
    delete ui;
}

void MainWindow::captureAndDisplayFrame(){
    QMutexLocker locker(&mutex);
    struct v4l2_buffer buf;
    memset(&buf, 0, sizeof(buf));
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.m.planes = planes.data(); //数组的大小
    buf.length = planes.size();   //平面数组中的元素数

    // 取
    if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) {
        qDebug() << "Failed to dequeue buffer:" << strerror(errno);
        return;
    }

    qDebug() << "Frame" << frameCount << "Buffer index:" << buf.index << "Size:" << buf.m.planes[0].bytesused;

    // Verify buffer data size
    if (buf.m.planes[0].bytesused != WIDTH * HEIGHT * 2) {
        qDebug() << "Warning: Buffer data size mismatch (" << buf.m.planes[0].bytesused << " bytes, expected " << WIDTH * HEIGHT * 2 << ")";
    }

    // Convert UYVY to RGB for display(qt)
    SwsContext *rgb_sws_ctx = sws_getContext(WIDTH, HEIGHT, AV_PIX_FMT_UYVY422,
                                             WIDTH, HEIGHT, AV_PIX_FMT_RGB24,
                                             SWS_BILINEAR, NULL, NULL, NULL);
    if (!rgb_sws_ctx) {
        qDebug() << "Failed to initialize RGB conversion context";
        return;
    }

    uint8_t *src_data[1] = { static_cast<uint8_t*>(global_buffers[buf.index].start) };
    int src_linesize[1] = { WIDTH * 2 };
    uint8_t *dst_data[1] = { new uint8_t[WIDTH * HEIGHT * 3] };
    int dst_linesize[1] = { WIDTH * 3 };

    //UYVY422 --> RGB24
    sws_scale(rgb_sws_ctx, src_data, src_linesize, 0, HEIGHT, dst_data, dst_linesize);

    /*
    用于qt显示
    */
    // Create QImage 对象 from RGB(24) data 
    QImage image(dst_data[0], WIDTH, HEIGHT, QImage::Format_RGB888);//图像数据 宽度 高度 格式

    //将图像显示在 QLabel 中               将图像缩放到指定的大小  获取 QLabel 的大小 在缩放时保持图像的宽高比
    ui->label->setPixmap(QPixmap::fromImage(image.scaled(ui->label->size(), Qt::KeepAspectRatio)));

    /*
    用于MP4显示
    */
    // Encode and write frame to MP4 if recording
    if (isRecording) {
        encode_and_write_frame(src_data[0], frameCount);
    }

    // Clean up
    sws_freeContext(rgb_sws_ctx);
    delete[] dst_data[0];

    // Re-enqueue
    if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) {
        qDebug() << "Failed to re-enqueue buffer:" << strerror(errno);
        return;
    }

    frameCount++;
}

void MainWindow::snapshot(){
    QMutexLocker locker(&mutex);

    struct v4l2_buffer buf;
    memset(&buf, 0, sizeof(buf));
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.m.planes = planes.data();
    buf.length = planes.size();

    //取
    if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) {
        qDebug() << "Failed to dequeue buffer for snapshot:" << strerror(errno);
        return;
    }

    QDir dir("/home/orangepi/saved_picture");
    QString filename = dir.filePath(QString("snapshot_%1.jpg").arg(QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss")));
    //                                    内存映射的缓冲区
    if(save_snapshot(static_cast<uint8_t *>(global_buffers[buf.index].start), filename.toStdString().c_str()) == 0){
        qDebug() << "Snapshot saved as" << filename;
    } else {
        qDebug() << "Failed to save snapshot";
    }

    // Re-enqueue buffer
    if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) {
        qDebug() << "Failed to re-enqueue buffer:" << strerror(errno);
    }
}

void MainWindow::startRecording(){
    QMutexLocker locker(&mutex);

    if(isRecording){
        qDebug() << "Already recording";
        return ;
    }

    QDir videoDir("/home/orangepi/saved_video");
    QString filename = videoDir.filePath(QString("video_%1.mp4")
                                        .arg(QDateTime::currentDateTime().toString("yyyyMMdd_HHmmss")));
    if(ffmpeg_init(filename.toStdString().c_str()) == -1){
        qDebug() << "Failed to initialize FFmpeg";
        exit(-1);
    }

    isRecording = true;
    frameCount = 0;
    ui->recordButton->setEnabled(false);
    ui->stopButton->setEnabled(true);
    qDebug()<< "Started recording to" <<filename;
}

void MainWindow::stopRecording(){
    QMutexLocker locker(&mutex);

    if(!isRecording){
        qDebug() << "Not recording";
        return ;
    }

    isRecording = false;
    ffmpeg_cleanup();
    frameCount = 0;
    ui->recordButton->setEnabled(true);
    ui->stopButton->setEnabled(false);
    qDebug()<< "Stop recording ";
}
